package com.kancy.okhttp.client.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.kancy.okhttp.client.support.ApplicationContextHolder;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * HttpMongoLog4jAppender
 *
 * @author kancy
 * @date 7/11/2019 20:12
 */
@Plugin(name = "HttpMongoLog4jAppender", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
public final class HttpMongoLog4jAppender extends AbstractAppender {

    /**
     * Builds HttpMongoLog4jAppender instances.
     *
     * @param <B> The type to build
     */
    public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
            implements org.apache.logging.log4j.core.util.Builder<HttpMongoLog4jAppender> {
        @Override
        public HttpMongoLog4jAppender build() {
            return new HttpMongoLog4jAppender(getName(), getLayout(), getFilter(), isIgnoreExceptions(), getPropertyArray());
        }
    }

    /**
     * @return a builder for a HttpMongoLog4jAppender.
     */
    @PluginBuilderFactory
    public static <B extends Builder<B>> B newBuilder() {
        return new Builder<B>().asBuilder();
    }

    private HttpMongoLog4jAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
                                   final boolean ignoreExceptions, final Property[] properties) {
        super(name, filter, layout, ignoreExceptions, properties);
        Objects.requireNonNull(layout, "layout");
    }

    @Override
    public void start() {
        super.start();
    }

    @Override
    public void append(final LogEvent event) {
        try {
            HttpRequestLogRepository logRepository = getHttpRequestLogRepository();
            if (Objects.nonNull(logRepository)){
                logRepository.save(getHttpRequestLog(event));
            }
        } catch (final Exception e) {
            error("Unable to save log in appender [" + getName() + "]", event, e);
        }
    }

    @Override
    public boolean stop(final long timeout, final TimeUnit timeUnit) {
        setStopping();
        boolean stopped = super.stop(timeout, timeUnit, false);
        setStopped();
        return stopped;
    }

    @Override
    public String toString() {
        return "HttpMongoLog4jAppender{" +
                "name=" + getName() +
                ", state=" + getState() +
                '}';
    }

    /**
     * 获取HttpRequestLogRepository
     * @return
     */
    private HttpRequestLogRepository getHttpRequestLogRepository() {
        HttpRequestLogRepository logRepository = null;
        try {
            logRepository = ApplicationContextHolder.getApplicationContext()
                    .getBean(HttpRequestLogRepositoryImpl.class);
        } catch (Exception e) {
            // ignore
        }
        return logRepository;
    }

    /**
     * getHttpRequestLog
     * @param event
     * @return
     */
    private HttpRequestLog getHttpRequestLog(LogEvent event) {
        HttpRequestLog httpRequestLog = JSON.parseObject(event.getMessage().getFormattedMessage(),
                new TypeReference<HttpRequestLog>() {});
        httpRequestLog.setId(UUID.randomUUID().toString());
        httpRequestLog.setLogLevel(event.getLevel().name());
        httpRequestLog.setCreateTime(LocalDateTime.now());
        return httpRequestLog;
    }
}
